package com.example.webfluxdemo.utils;

import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.Map;
import java.util.Optional;

import static org.springframework.http.MediaType.APPLICATION_JSON;

/**
 * Created by victor on 2021/10/11 15:16.
 */
@Slf4j
public class WebClientUtils {
    private static final WebClient WEB_CLIENT = WebClient.create();

    @Data
    @Builder
    public static class Request {
        private String url;         // 必填
        private HttpMethod method;  // 必填
        private MediaType mediaType;    // 默认application/json
        private Map<String, String> headers;
        private Publisher<?> body;  // 与bodyValue二选一
        private Class<?> bodyClass; // body不为空，必填
        private Object bodyValue;   // 与body二选一，优先取body
        private Integer timeout;    // 默认500，单位：毫秒
    }

    public static WebClient.RequestBodyUriSpec requestBodyUriSpec(Request request) {
        WebClient.RequestBodyUriSpec spec = WEB_CLIENT.method(request.getMethod());
        spec.uri(request.getUrl())
                .headers(httpHeaders -> {
                    httpHeaders.setContentType(Optional.ofNullable(request.getMediaType()).orElse(APPLICATION_JSON));
                    if (!CollectionUtils.isEmpty(request.getHeaders())) {
                        request.getHeaders().forEach(httpHeaders::add);
                    }
                });
        if (request.getBody() != null) {
            spec.body(request.getBody(), request.getBodyClass());
        } else if (request.getBodyValue() != null) {
            spec.bodyValue(request.getBodyValue());
        }
        return spec;
    }

    public static Mono<String> request(Request request) {
        WebClient.RequestBodyUriSpec spec = requestBodyUriSpec(request);
        return spec.exchangeToMono(res -> {
            log.info("In exchangeToMono");
            if (res.statusCode() == HttpStatus.OK) {
                return res.bodyToMono(String.class);
            }
            return Mono.empty();
        })
        .timeout(Duration.ofMillis(Optional.ofNullable(request.getTimeout()).orElse(500)));
    }
}
